/**********************************************************************************\
** 文件名称: flash.c
** 创建人员: YCG
** 创建日期: 2019-01-18
** 文档描述:
** 版本代号: V0.1
** 版本说明: 初始版本
\********************************************************************************/

#include "flash.h"

Flash_t *NowDMAFlash = NULL; //当前DMA正在传输的Flash

static void Flash_WriteEnable(Flash_t *yFlash);
static uint32_t FLASH_ReadID(Flash_t *yFlash);
static uint8_t FLASH_ReadDeviceID(Flash_t *yFlash);

/*******************************************************************************
** 函数名称: Flash_Init
** 功能描述: 初始化
** 参数说明: ySPI:      [输入] 使用的SPI端口
**			     yGPIO:     [输入] 引脚端口
**			     yGPIO_Pin: [输入] 引脚
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
Flash_t * Flash_Init(SPI_HandleTypeDef *ySPI, GPIO_TypeDef *yGPIO,  uint16_t yGPIO_Pin) {

    Flash_t *yFlash = MyMalloc(sizeof(Flash_t));   //获取内存空间
    if(yFlash != NULL) {                    //如果成功分配到空间

        yFlash->SPI = ySPI;                 //初始化SPI对象
        yFlash->GPIO = yGPIO;               //初始化引脚端口
        yFlash->GPIO_Pin = yGPIO_Pin;       //初始化引脚
        yFlash->TimeOut =  Flash_TimeOut;   //初始化超时时间
        yFlash->PageSize = W25X_PageSize;   //初始化页面大小
        yFlash->State = Flash_NoInit;       //将状态设置为未初始化

        uint32_t yNowTime = HAL_GetTick();  //获取当前的系统时间

        Flash_CS_Enable(yFlash);
        Flash_Byte(yFlash, W25X_ReadStatusReg);                 //读取状态寄存器
        while(Flash_Byte(yFlash, DUMMY_BYTE)&BUSY_Flag) {       //等待芯片准备好
            if((HAL_GetTick() - yNowTime) > yFlash->TimeOut) {  //判断是否超时
                break;
            }
        }
        Flash_CS_Disable(yFlash);

        yFlash->DeviceID = FLASH_ReadDeviceID(yFlash);	        //获取设备ID

        uint32_t JedecID = FLASH_ReadID(yFlash);               //获取芯片型号
        yFlash->Type = (uint8_t) (JedecID >> 8);
        yFlash->Capacity = (uint8_t) (1 << ((JedecID & 0x0F) - 1)); //换算成容量，单位为M

        if((JedecID != DUMMY_WORD) || (yFlash->DeviceID != DUMMY_BYTE)) { //如果成功读取到数据
            yFlash->State = Flash_Ready;    //设置芯片为就绪
        }
        FLASH_Printf("Flash : 0x%8x\n", yFlash);
        FLASH_Printf("Flash->DeviceID : 0x%x\n", yFlash->DeviceID);
        FLASH_Printf("Flash->Capacity : %dM\n", yFlash->Capacity);
    }
    return yFlash;
}

/*******************************************************************************
** 函数名称: Flash_Byte
** 功能描述: 发送或者读取一字节数据
** 参数说明: yFlash: [输入/出]
**			     sendByte: [输入/出] 发送的数据
** 返回说明: 读取的数据
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t Flash_Byte(Flash_t *yFlash, uint8_t sendByte)
{
    SPI_HandleTypeDef *hspi = yFlash->SPI;
    uint8_t readByte = DUMMY_BYTE;
	  uint32_t yNowTime = HAL_GetTick();  //获取当前的系统时间
    //  HAL_SPI_TransmitReceive(yFlash->SPI, &sendByte, &readByte, 1, 0xFFFFFF);//很耗时，缩减一下
    __HAL_LOCK(hspi);
    if(hspi->State == HAL_SPI_STATE_READY)
    {
        __HAL_SPI_ENABLE(hspi);
        hspi->Instance->DR = sendByte;   //将数据写入发送寄存器
        while(!(__HAL_SPI_GET_FLAG(hspi, SPI_SR_RXNE))) {
					      if((HAL_GetTick() - yNowTime) > yFlash->TimeOut) {  //判断是否超时
                break;
        }}   //等待接收完成
        readByte = hspi->Instance->DR;   //读取数据
    }
    __HAL_UNLOCK(hspi);
    return readByte;
}


/*******************************************************************************
** 函数名称: Flash_WaitReady
** 功能描述: 等待SPI和芯片准备好
** 参数说明: yFlash: [输入/出]
** 返回说明: 0：超时
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t Flash_WaitReady(Flash_t *yFlash) {

    uint32_t yNowTime = HAL_GetTick();
    uint8_t yReturn = Flash_BUSY;
    while(	HAL_SPI_GetState(yFlash->SPI) != HAL_SPI_STATE_READY) {   //等待SPI准备好
        if((HAL_GetTick() - yNowTime) > yFlash->TimeOut) {
            return yReturn;
        }
    }
    Flash_CS_Enable(yFlash);
    Flash_Byte(yFlash, W25X_ReadStatusReg);             //读取状态寄存器
    while(Flash_Byte(yFlash, DUMMY_BYTE)&BUSY_Flag) {    //等待芯片准备好
        if((HAL_GetTick() - yNowTime) > yFlash->TimeOut) {
            return yReturn;
        }
    }
    yReturn = Flash_SUCCESS;
    Flash_CS_Disable(yFlash);
    return yReturn;
}

/*******************************************************************************
** 函数名称: FLASH_SectorErase
** 功能描述: 擦除扇区
**
** 串行Flash最小擦除块大小为4KB(4096字节)，即一个扇区大小，要求输入参数
** 为4096倍数。在往串行Flash芯片写入数据之前要求先擦除空间。
**
** 参数说明: yFlash: [输入/出]
**			 ySectorAddr: [输入] 待擦除扇区地址，要求为4096倍数
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t FLASH_SectorErase(Flash_t *yFlash, uint32_t ySectorAddr)
{
    uint8_t* yAddr = (uint8_t*)&ySectorAddr;    //将块地址转换为8位数据
    uint8_t yReturn = Flash_WaitReady(yFlash);
    if(yReturn == Flash_SUCCESS) {
        Flash_WriteEnable(yFlash);    /* 发送FLASH写使能命令 */
        Flash_WaitReady(yFlash);      /* 等待准备好 */

        Flash_CS_Enable(yFlash);

        Flash_Byte(yFlash, W25X_SectorErase); /* 发送扇区擦除指令*/

        Flash_Byte(yFlash, yAddr[2]); /* 发送高位地址 */
        Flash_Byte(yFlash, yAddr[1]); /* 发送中位地址 */
        Flash_Byte(yFlash, yAddr[0]); /* 发送低地址 */

        Flash_CS_Disable(yFlash);
    }
    return yReturn;
}

/*******************************************************************************
** 函数名称: FLASH_BulkErase
** 功能描述: 擦除串行Flash整片空间
** 参数说明: yFlash: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t FLASH_BulkErase(Flash_t *yFlash)
{
    uint8_t yReturn = Flash_WaitReady(yFlash);
    if(yReturn == Flash_SUCCESS) {
        Flash_WriteEnable(yFlash);   /* 发送FLASH写使能命令 */

        Flash_CS_Enable(yFlash);

        Flash_Byte(yFlash, W25X_ChipErase);

        Flash_CS_Disable(yFlash);

    }
    return yReturn;
}

/*******************************************************************************
** 函数名称: FLASH_PageWrite
** 功能描述: 往串行FLASH按页写入数据，调用本函数写入数据前需要先擦除扇区
** 参数说明: yFlash: [输入/出]
**			 yBuffer:    [输入] 待写入数据的指针
**			 yWriteAddr: [输入] 写入地址
**			 ySize:      [输入] 写入数据长度，必须小于等于SPI_FLASH_PerWritePageSize
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t FLASH_PageWrite(Flash_t *yFlash, uint8_t* yBuffer, uint32_t yWriteAddr, uint16_t ySize)
{
    uint8_t* yAddr = (uint8_t*)&yWriteAddr;

    uint8_t yReturn = Flash_WaitReady(yFlash);
    if(yReturn == Flash_SUCCESS) {
        Flash_WriteEnable(yFlash);

        Flash_CS_Enable(yFlash);

        Flash_Byte(yFlash, W25X_PageProgram);   /* 写送写指令*/

        Flash_Byte(yFlash, yAddr[2]); /* 发送高位地址 */
        Flash_Byte(yFlash, yAddr[1]); /* 发送中位地址 */
        Flash_Byte(yFlash, yAddr[0]); /* 发送低地址 */

        HAL_SPI_Transmit_DMA(yFlash->SPI, yBuffer, ySize);
        NowDMAFlash = yFlash;
    }
    return yReturn;
}

/*******************************************************************************
** 函数名称: FLASH_BufferWrite
** 功能描述: 往串行FLASH写入数据，调用本函数写入数据前需要先擦除扇区
** 参数说明: yFlash: [输入/出]
**			 yBuffer:    [输入] 待写入数据的指针
**			 yWriteAddr: [输入] 写入地址
**			 ySize:      [输入] 写入数据长度，该函数可以设置任意写入数据长度
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t FLASH_BufferWrite(Flash_t *yFlash, uint8_t* yBuffer, uint32_t yWriteAddr, uint16_t ySize)
{
    uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, AlignCount = 0;
    uint16_t   yPageSize = yFlash->PageSize;  //页大小

    Addr = yWriteAddr % yPageSize;   //计算写入数据与块地址是否对齐
    AlignCount = yPageSize - Addr;   //计算出未对齐的数据差
    uint8_t yReturn = Flash_BUSY;

    if(ySize > AlignCount) {              //判断写入总大小是否大于未对齐的数据差
        ySize -= AlignCount;              //总的数据量减去未对齐的数据差

        NumOfPage   =  ySize / yPageSize;   //页数
        NumOfSingle =  ySize % yPageSize;   //剩余量
    }

    if(AlignCount > 0) {
        yReturn = FLASH_PageWrite(yFlash, yBuffer, yWriteAddr, AlignCount); //先将第一页补齐
        if(yReturn == Flash_BUSY)  return yReturn;
        yBuffer += AlignCount;                   //源数据地址增加
        yWriteAddr += AlignCount;                //Flash地址增加
    }

    while(NumOfPage--) {
        yReturn = FLASH_PageWrite(yFlash, yBuffer, yWriteAddr, yPageSize); //写入一页
        if(yReturn == Flash_BUSY)  return yReturn;
        yBuffer += yPageSize;                   //源数据地址增加
        yWriteAddr += yPageSize;                //Flash地址增加
    }

    if(NumOfSingle > 0) {
        yReturn = FLASH_PageWrite(yFlash, yBuffer, yWriteAddr, NumOfSingle);
    }
    return yReturn;
}

/*******************************************************************************
** 函数名称: FLASH_BufferRead
** 功能描述: 从串行Flash读取数据
** 参数说明: yFlash: [输入/出]
**			 yBuffer:   [输出] 存放读取到数据的指针
**			 yReadAddr: [输入] 读取数据目标地址
**			 ySize:     [输入] 读取数据长度
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t FLASH_BufferRead(Flash_t *yFlash, uint8_t* yBuffer, uint32_t yReadAddr, uint16_t ySize)
{
    uint8_t yReturn ;
    FLASH_StartRead(yFlash, yReadAddr);
    yReturn = HAL_SPI_Receive_DMA(yFlash->SPI, yBuffer, ySize);

    uint32_t yNowTime = HAL_GetTick();
    while(	HAL_SPI_GetState(yFlash->SPI) != HAL_SPI_STATE_READY) { //等待SPI准备好
        if((HAL_GetTick() - yNowTime) > yFlash->TimeOut) {
            yReturn = Flash_BUSY;
            break;
        }
    }
    Flash_CS_Disable(yFlash);
    return yReturn;
}

/*******************************************************************************
** 函数名称: FLASH_StartReadSequence
** 功能描述: 启动连续读取数据串
** 参数说明: yFlash: [输入/出]
**			 yReadAddr: [输入] 读取地址
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t FLASH_StartRead(Flash_t *yFlash, uint32_t yReadAddr)
{
    uint8_t* yAddr = (uint8_t*)&yReadAddr;
    uint8_t yReturn = Flash_WaitReady(yFlash);
    if(yReturn == Flash_SUCCESS) {
        Flash_CS_Enable(yFlash);  /* 选择串行FLASH: CS低电平 */
        Flash_Byte(yFlash, W25X_ReadData);/* 发送 读 指令 */

        Flash_Byte(yFlash, yAddr[2]); /* 发送高位地址 */
        Flash_Byte(yFlash, yAddr[1]); /* 发送中位地址 */
        Flash_Byte(yFlash, yAddr[0]); /* 发送低地址 */
    }
    return yReturn;
}


/*******************************************************************************
** 函数名称: Flash_PowerDown
** 功能描述: 进入掉电模式
** 参数说明: yFlash: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t Flash_PowerDown(Flash_t *yFlash)
{
    uint8_t yReturn = Flash_WaitReady(yFlash);
    if(yReturn == Flash_SUCCESS) {
        Flash_CS_Enable(yFlash);
        Flash_Byte(yFlash, W25X_PowerDown);
        Flash_CS_Disable(yFlash);
    }
    return yReturn;
}

/*******************************************************************************
** 函数名称: Flash_WriteEnable
** 功能描述:
** 参数说明: yFlash: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
static void Flash_WriteEnable(Flash_t *yFlash)
{
    Flash_CS_Enable(yFlash);
    Flash_Byte(yFlash, W25X_WriteEnable);
    Flash_CS_Disable(yFlash);
}
/*******************************************************************************
** 函数名称: Flash_WAKEUP
** 功能描述: 唤醒串行Flash
** 参数说明: yFlash: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
uint8_t Flash_WakeUp(Flash_t *yFlash)
{
    uint8_t yReturn = Flash_WaitReady(yFlash);
    if(yReturn == Flash_SUCCESS) {
        Flash_CS_Enable(yFlash);
        Flash_Byte(yFlash, W25X_ReleasePowerDown);
        Flash_CS_Disable(yFlash);
    }
    return yReturn;
}

/*******************************************************************************
** 函数名称: FLASH_ReadID
** 功能描述: 读取串行Flash型号的ID
** 参数说明: yFlash: [输入/出]
** 返回说明: 串行Flash的型号ID
                FLASH_ID      IC型号      存储空间大小
                0xEF3015      W25X16        2M byte
                0xEF4015	    W25Q16        4M byte
                0XEF4017      W25Q64        8M byte
                0XEF4018      W25Q128       16M byte  (YS-F1Pro开发板默认配置)
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
static uint32_t FLASH_ReadID(Flash_t *yFlash)
{
    uint32_t Temp = DUMMY_WORD;
    uint8_t* yTemp = (uint8_t*) &Temp;
    Flash_CS_Enable(yFlash);  /* 选择串行FLASH: CS低电平 */
    Flash_Byte(yFlash, W25X_JedecDeviceID);  /* 发送命令：读取芯片型号ID */

    yTemp[2] = Flash_Byte(yFlash, DUMMY_BYTE);   /* 从串行Flash读取一个字节数据 */
    yTemp[1] = Flash_Byte(yFlash, DUMMY_BYTE);   /* 从串行Flash读取一个字节数据 */
    yTemp[0] = Flash_Byte(yFlash, DUMMY_BYTE);   /* 从串行Flash读取一个字节数据 */

    Flash_CS_Disable(yFlash);    /* 禁用串行Flash：CS高电平 */
    return Temp;
}


/*******************************************************************************
** 函数名称: FLASH_ReadDeviceID
** 功能描述: 读取串行Flash设备ID
** 参数说明: yFlash: [输入/出]
** 返回说明: 串行Flash的设备ID
** 创建人员: YCG
** 创建日期: 2019-01-18
********************************************************************************/
static uint8_t FLASH_ReadDeviceID(Flash_t *yFlash)
{
    uint8_t Temp = DUMMY_BYTE;

    Flash_CS_Enable(yFlash);   /* 选择串行FLASH: CS低电平 */

    /* 发送命令：读取芯片设备ID * */
    Flash_Byte(yFlash, W25X_DeviceID);
    Flash_Byte(yFlash, DUMMY_BYTE);
    Flash_Byte(yFlash, DUMMY_BYTE);
    Flash_Byte(yFlash, DUMMY_BYTE);

    Temp = Flash_Byte(yFlash, DUMMY_BYTE);  /* 从串行Flash读取一个字节数据 */

    Flash_CS_Disable(yFlash);   /* 禁用串行Flash：CS高电平 */
    return Temp;
}

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if(NowDMAFlash->SPI == hspi) {
        Flash_CS_Disable(NowDMAFlash);   /* 禁用串行Flash：CS高电平 */
    }
}


/********************************End of File************************************/

